<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
    <script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.js"></script>
    <script>
        var shardSize = 5 * 1024 * 1024;//分片大小
        var onsubmitFiles = {};//带上传的文件
        var requestList = {};//请求列表
        var START = "开始上传";
        var STOP = "暂停上传";
        //选择文件列表展示
        function listFiles(fileList) {
            let html = [];
            Object.keys(fileList).forEach(k=>{
                var file = fileList[k];
                html.push("<tr>");
                html.push("<td><progress name="+k+" max='100' value='0'></progress></td>");
                html.push("<td>"+file.name+"</td>");
                html.push("<td class='tip' name="+k+">未上传</td>");
                html.push("<td><button disabled='true' name="+k+" onclick='clickButton("+k+")'>"+START+"</button></td>");
                html.push("</tr>");
            })
            $("#container").html($(html.join("")));
        }
        // 单个上传按钮 点击事件
        function clickButton(k) {
            var status = $("button[name="+k+"]").text();
            switch(status) {
                case START:
                    startUpload(k)
                    break;
                case STOP :
                    abortUpload(k);
                    break;
                default:
                    console.error("未知操作",status);
            }
        }
        //停止上传
        function abortUpload(k) {
            onsubmitFiles[k].stop = true;
            $("button[name="+k+"]").text(START);
        }
        //开始上传
        function startUpload(k) {
            onsubmitFiles[k].stop = false;
            var f = onsubmitFiles[k];
            uploadChunk(k,f.fileMd5,f.file,f.chunks,0);
            $("button[name="+k+"]").text(STOP);
        }
        //上传进度设置
        function setProgress(k,currentSize,totalSize) {
            var progress = currentSize/totalSize*100;
            progress = Math.round(progress*100)/100;
            $("progress[name='"+k+"']").attr("value",progress);
            if(progress!==0) {
                $(".tip[name='"+k+"']").text(progress+"%");
            } 
            
            if(progress >= 100) {
                $("button[name="+k+"]").attr("disabled","true");
                $("button[name="+k+"]").text("上传完毕");
            }
        } 
        function setProgressByChunk(k,size) {
            onsubmitFiles[k].uploadedSize = onsubmitFiles[k].uploadedSize + size;
            var currentSize = onsubmitFiles[k].uploadedSize;
            var totalSize = onsubmitFiles[k].file.size;
            setProgress(k,currentSize,totalSize);
        }
        //校验文件上传状态并回显上传进度
        function validFileProgress(fileList) {
            Object.keys(fileList).forEach(k=>{
                var file = fileList[k];
                md5File(file,r=>{
                    $.ajax({
                        async:true,
                        type:"post",
                        url:"/validFileProgress",
                        data:{
                            fileMd5:r,
                            name:file.name
                        },
                        dataType:'json',
                        success(res) {
                            $("button[name="+k+"]").removeAttr("disabled")
                            var start;
                            if(res.file) {
                                start = res.file.size;
                                setProgress(k,start,file.size);
                            }  else {
                                start = 0;
                            }
                            
                            splitFile(k,r,file,start);
                        },
                        error(e) {
                            console.log(e);
                            //TODO
                        }
                    })
                });
                
            })
        }
        //分片MD5序列化文件
        function md5File(file,callback) {
            var value = file.name+file.lastModified+file.size;
            callback(md5(value));
        }

        //选择文件事件
        function onSelectFile(e) {
            var fileList = $("#file")[0].files;
            onsubmitFiles=[];
            listFiles(fileList);
            validFileProgress(fileList);
        }
        /**
         * 文件分片
         * @fileMd5 文件MD5值
         * @file 文件
         * @start 起始上传位置
        */
        function splitFile(key,fileMd5,file,start) {
            var size = file.size;
            
            var shardcount = Math.ceil((size-start)/shardSize);
            var chunks = [];
            for(var i=0; i<shardcount;i++) {
                var begin = i * shardSize+start;
                var end = Math.min(size, begin + shardSize);
                chunks.push({
                    data:file.slice(begin,end),
                    status: 0,//0 待上传 1 上传中 2 上传完毕
                });
            };
            onsubmitFiles[key] = {
                stop:false,//是否暂停上传
                uploadedSize:start,//已上传部分的大小
                file,//原始文件
                fileMd5,//文件MD5值.
                chunks,//分片数据
            };
        }
        //上传文件
        function submit(e) {
            Object.keys(onsubmitFiles)&&Object.keys(onsubmitFiles).forEach(k=>{
                $("button[name="+k+"]").text(STOP);
                onsubmitFiles[k].stop = false;
                var f = onsubmitFiles[k];
                uploadChunk(k,f.fileMd5,f.file,f.chunks,0,f.uploadedSize);
            })
        };
        /**
         * 文件分片上传
         * @key 文件在显示列表位置
         * @fileMd5 文件MD5值
         * @file 文件
         * @chunks 文件分片
         * @index 起始上传位置
        */
        function uploadChunk(key,fileMd5,file,chunks,index) {
            if(onsubmitFiles[key].stop) return;
            if(chunks.length==0) {
                setProgress(key,file.size,file.file);
                return
            }
            var chunk = chunks[index];
            if(chunk.status === 0) {
                var form = new FormData();
                form.append("chunk",chunk.data)
                form.append("chunkLength",chunks.length)
                form.append("index",index)
                form.append("fileMd5",fileMd5);
                form.append("name",file.name);
                form.append("totalSize",file.size);
                chunk.status = 1;
                $.ajax({
                    async:true,
                    type:"post",
                    url:"/upload",
                    data:form,
                    dataType:"json",
                    processData:false,
                    contentType:false,
                    success(res) {
                        if(res.ok) {
                            chunk.status = 2
                            if(index == chunks.length-1) {
                                console.log("上传完毕");
                            } else {
                                uploadChunk(key,fileMd5,file,chunks,index+1);//递归上传下一分片
                            }
                            setProgressByChunk(key,chunk.data.size);
                        } else {
                            chunk.status = 0
                            //TOOD 提示上传失败
                        }
                    },
                    error(e) {
                        chunk.status = 0;
                        console.log(e);
                    }
                })
                
            } else {
                if(index == chunks.length-1) {
                    console.log("上传完毕")
                } else {
                    uploadChunk(key,fileMd5,file,chunks,index+1);//递归上传下一分片
                }
            }
        }
    </script>
</head>
<body>
    <h1>h5 文件断点续传</h1>
    <button>
        <label for="file">选择文件</label>
        <input style="display:none" onchange="onSelectFile()" id="file" type="file" multiple/>
    </button>
    
    <button id="uploadAll" onclick="submit()">全部上传</button>
    <table id="container">

    </table>
    
    
</body>
</html>